/*
 * Copyright (c) 2011-2024, Cpjit (cpjit@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.cpjit.data.web;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestBody;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.cpjit.data.web.MyBatisPlusPageHandlerMethodArgumentResolver.parseOrderItem;


/**
 * 作为 {@link MyBatisPlusPageHandlerMethodArgumentResolver}的补充，从请求体里面获取分页参数。
 *
 * @author yonghuan[cpjit@qq.com]
 * yong
 */
@Aspect
@Slf4j
@Setter
public class RequestBodyPageArgumentResolver {

    private static final String DEFAULT_PAGE_PARAMETER = "page";
    private static final String DEFAULT_SIZE_PARAMETER = "size";
    private static final String DEFAULT_SORT_PARAMETER = "sort";
    private String pageParameterName = DEFAULT_PAGE_PARAMETER;
    private String sizeParameterName = DEFAULT_SIZE_PARAMETER;
    private String sortParameterName = DEFAULT_SORT_PARAMETER;
    private int defaultPageSize = 20;
    private boolean oneIndexedParameters = false;

    @Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public Object resolvePage(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Parameter[] parameters = methodSignature.getMethod().getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            // 找到被@RequestBody标记的参数
            if (parameter.getAnnotation(RequestBody.class) != null) {
                Object arg = args[i];
                // 如果@RequestBody标记的参数是PageQuery的实例
                PropertyDescriptor pagePropertyDescriptor = BeanUtils.getPropertyDescriptor(arg.getClass(), pageParameterName);
                PropertyDescriptor sizePropertyDescriptor = BeanUtils.getPropertyDescriptor(arg.getClass(), sizeParameterName);
                if (pagePropertyDescriptor != null && sizePropertyDescriptor != null) {
                    Integer pageNo = (Integer) pagePropertyDescriptor.getReadMethod().invoke(arg);
                    Integer pageSize = (Integer) sizePropertyDescriptor.getReadMethod().invoke(arg);
                    PropertyDescriptor sortParameter = BeanUtils.getPropertyDescriptor(arg.getClass(), sortParameterName);
                    Sort sort = sortParameter != null ? (Sort) sortParameter.getReadMethod().invoke(arg) : Sort.unsorted();
                    Arrays.stream(((MethodSignature) pjp.getSignature()).getMethod().getParameters())
                            .filter(p -> p.getType().isAssignableFrom(IPage.class)).map(p -> ((ParameterizedType) p.getParameterizedType())).map(parameterizedType -> (Class<?>) parameterizedType.getActualTypeArguments()[0])
                            .findAny()
                            .ifPresent(entityClazz -> resolvePage(entityClazz, pageNo, pageSize, sort, args));
                }
                break;
            }
        }
        return pjp.proceed(args);
    }

    private void resolvePage(Class<?> clazz, Integer pageNo, Integer pageSize, Sort sort, Object[] args) {
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof IPage) {
                Page<?> page = new Page<>();
                if (pageNo != null) {
                    page.setCurrent(pageNo);
                } else {
                    page.setCurrent(oneIndexedParameters ? 1 : 0);
                }
                if (pageSize != null) {
                    page.setSize(pageSize);
                } else {
                    page.setSize(defaultPageSize);
                }
                // 解析排序参数
                if (sort != null) {
                    List<OrderItem> orderItems = sort.stream().map(order -> parseOrderItem(clazz, order)).filter(Objects::nonNull).collect(Collectors.toList());
                    page.setOrders(orderItems);
                }
                args[i] = page;
                return;
            }
        }
    }
}
